﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ttlang.compiler
{
    //此处处理函数内部代码的编译
    internal static class compiler_codeblock
    {
        public static void ParseNode_CodeBlock(CompilerContext context, src.AstNode innode, out node.Node_CodeBlock outnode)
        {
            //每入一層括號，加個棧
            context.PushBlock();

            outnode = new node.Node_CodeBlock();

            //该脱的早都脱过了
            ////兼容脱括号
            //if (innode.type == "code_block")
            //{
            //    innode = innode.children[1];
            //}

            //if (innode.type != "codedoc")
            //{
            //    throw new Exception("must be codedoc");
            //}

            if (innode.children != null)
            {
                for (var i = 1; i < innode.children.Count - 1; i++)
                {
                    var item = innode.children[i];
                    if (item.type == "codedoc")
                    {
                        foreach (var subitem in item.children)
                        {
                            ParseNode_CodeAtom(context, subitem, out node.Node_CodeAtom atom);
                            if (atom == null)
                                continue;//空行直接走，不要了
                            outnode.items.Add(atom);
                        }
                    }
                    else
                    {//有时候会有把codedoc直接展开的情况
                        ParseNode_CodeAtom(context, item, out node.Node_CodeAtom atom);
                        if (atom == null)
                            continue;//空行直接走，不要了
                        outnode.items.Add(atom);
                    }

                }
            }

            context.PopBlock();
        }
        public static void ParseNode_CodeAtom(CompilerContext context, src.AstNode innode, out node.Node_CodeAtom outnode)
        {
            outnode = null;
            //脱層
            if (innode.text == ";")
                return;
            if (innode.type == "code_atom")
            {
                innode = innode.children[0];
            }
            switch (innode.type)
            {
                case "define":
                    throw new NotImplementedException();
                case "COMMENTSINGLE":
                    {
                        var comment = new node.Node_Code_Comment();
                        comment.comment = innode.text;
                        comment.commonttype = node.CommentType.SINGLELINE;

                        outnode = comment;
                    }
                    break;
                case "COMMENTMUL":
                    {
                        var comment = new node.Node_Code_Comment();
                        comment.comment = innode.text;
                        comment.commonttype = node.CommentType.RANGE;
                        outnode = comment;
                    }

                    break;
                case "empty_line":
                    outnode = null;
                    break;
                case "codeelem"://直接脫

                    if (innode.children == null || innode.children.Count == 0)
                        break;
                    ParseNode_CodeElem(context, innode.children[0], out outnode);
                    break;
                case "code_vaild":
                    ParseNode_CodeAtom(context, innode.children[0], out outnode);
                    break;
                case "codeelem_value_def":
                    {
                        ParseNode_CodeElem_ValueDef(context, innode, out node.Node_Code_ValueDef _node);
                        outnode = _node;
                    }
                    break;
                case "codeelem_set":
                    {
                        ParseNode_CodeElem_Set(context, innode, out node.Node_Code_Set _node);
                        outnode = _node;
                    }
                    break;
                case "codeelem_selfop":
                    {
                        ParseNode_CodeElem_SelfOp(context, innode, out var _node);
                        outnode = _node;
                    }
                    break;
                case "codeelem_expr":
                    {
                        ParseNode_CodeElem_Expr(context, innode, out var _node);
                        outnode = _node;
                    }
                    break;

                case "code_return":
                    {
                        ParseNode_CodeElem_Return(context, innode, out var _node);
                        outnode = _node;
                    }
                    break;
                case "code_block_if":
                    {
                        ParseNode_CodeIf(context, innode, out node.Node_Code_If _node);
                        outnode = _node;
                    }
                    break;
                case "code_block_for":
                    {
                        ParseNode_CodeFor(context, innode, out node.Node_Code_For _node);
                        outnode = _node;
                    }
                    break;
                case "code_block_while":
                    throw new NotImplementedException();
                case "code_block_dowhile":
                    throw new NotImplementedException();
                case "code_block_switch":
                    throw new NotImplementedException();
                case "code_block":
                    {
                        ParseNode_CodeBlock(context, innode, out var _node);
                        outnode = _node;
                    }
                    break;
                default:
                    throw new NotImplementedException();

            }
        }
        //普通的代碼行
        public static void ParseNode_CodeElem(CompilerContext context, src.AstNode innode, out node.Node_CodeAtom outnode)
        {
            switch (innode.type)
            {
                case "codeelem_value_def":
                    {
                        ParseNode_CodeElem_ValueDef(context, innode, out node.Node_Code_ValueDef _node);
                        outnode = _node;
                    }
                    break;
                case "codeelem_set":
                    {
                        ParseNode_CodeElem_Set(context, innode, out node.Node_Code_Set _node);
                        outnode = _node;
                    }
                    break;
                case "codeelem_selfop":
                    {
                        ParseNode_CodeElem_SelfOp(context, innode, out var _node);
                        outnode = _node;
                    }
                    break;
                case "codeelem_expr":
                    {
                        ParseNode_CodeElem_Expr(context, innode, out var _node);
                        outnode = _node;
                    }
                    break;

                default:
                    throw new NotImplementedException();

            }
        }
        public static void ParseNode_CodeElem_Return(CompilerContext context, src.AstNode innode, out node.Node_Code_Return outnode)
        {
            node.Node_Code_Return _return = new node.Node_Code_Return();
            if (innode.children.Count > 1)
            {//with value
                compiler_expr.ParseNode_Expr(context, innode.children[1], out node.Node_Expr _node);
                _return.vlaue = _node;
            }
            outnode = _return;
        }
        public static void ParseNode_CodeElem_Set(CompilerContext context, src.AstNode innode, out node.Node_Code_Set outnode)
        {
            node.Node_Code_Set def = new node.Node_Code_Set();
            def.id = innode.children[0].text;

            compiler_expr.ParseNode_Expr(context, innode.children[2], out node.Node_Expr _node);
            def.initvlaue = _node;

            outnode = def;
        }
        public static void ParseNode_CodeElem_ValueDef(CompilerContext context, src.AstNode innode, out node.Node_Code_ValueDef outnode)
        {
            node.Node_Code_ValueDef def = new node.Node_Code_ValueDef();
            //innode.children[0].type == "var";//should be var
            def.id = innode.children[1].text;//shoulde be id;
            int checkeqpos = 2;
            if (innode.children.Count < 4)
                throw new Exception("CodeElem 不能没有类型或初值" + " in:" + innode.text);
            if (innode.children[2].text == ":")//withtype
            {
                var deftype_name = innode.children[3].text;
                checkeqpos = 4;
                if (context.FindType(deftype_name, out var _type) == false || _type == null)
                {
                    throw new Exception("类型未知:" + _type + " in:" + innode.text);
                }

                def.deftype = _type;
            }

            if (innode.children.Count > checkeqpos + 1)
            {//with value
                compiler_expr.ParseNode_Expr(context, innode.children[checkeqpos + 1], out node.Node_Expr _node);
                def.initvlaue = _node;
            }

            //强类型语言，expr必须有明确类型，所以直接要就完了
            if (def.deftype == null)
            {
                def.deftype = def.initvlaue.tttype;
            }
            if (def.deftype == null)
            {
                throw new Exception("未能通過右值推斷類型");
            }
            ////如果沒有deftype_name,就需要根據右值推測
            //if (deftype_name == "")
            //{
            //    if (def.initvlaue == null)
            //        throw new Exception("无右值,无法推断类型");

            //    //这部分将来要重构，右值推测除了常量还有函数返回类型，复杂表达式推断等
            //    else if (def.initvlaue.type == node.NodeType.EXPR_NUMBER)
            //    {
            //        var num = def.initvlaue as node.Node_Expr_Number;
            //        if (num == null)
            //            throw new Exception("error type");

            //        if (num.numbertype == node.NodeType.INT
            //        || num.type == node.NodeType.INT_BIN
            //        || num.type == node.NodeType.INT_HEX)
            //        {
            //            deftype_name = "int32";
            //        }
            //        else
            //        {
            //            throw new NotSupportedException();

            //        }
            //    }
            //    else
            //    {
            //        throw new NotSupportedException();
            //    }
            //}


            context.RegTempVar(def.id, def.deftype);
            outnode = def;
        }
        public static void ParseNode_CodeElem_SelfOp(CompilerContext context, src.AstNode innode, out node.Node_Code_SelfOf outnode)
        {
            node.Node_Code_SelfOf self = new node.Node_Code_SelfOf();
            compiler_expr.ParseNode_Expr(context, innode.children[0], out self.id_expr);
            self.token = innode.children[1].text;
            if (innode.children.Count > 2)
                compiler_expr.ParseNode_Expr(context, innode.children[2], out self.rightvalue);
            outnode = self;

        }
        public static void ParseNode_CodeIf(CompilerContext context, src.AstNode innode, out node.Node_Code_If outnode)
        {
            node.Node_Code_If fif = new node.Node_Code_If();

            compiler_expr.ParseNode_Expr(context, innode.children[2], out fif.elem_cond);
            ParseNode_CodeAtom(context, innode.children[4], out fif.body);


            if (innode.children.Count > 5 && innode.children[5].text == "else")
            {
                ParseNode_CodeAtom(context, innode.children[6], out fif.elsepart);
            }
            outnode = fif;
        }
        public static void ParseNode_CodeFor(CompilerContext context, src.AstNode innode, out node.Node_Code_For outnode)
        {
            context.PushBlock();

            node.Node_Code_For ffor = new node.Node_Code_For();
            //0 for
            //1 (
            src.AstNode initelem;
            src.AstNode condelem;
            src.AstNode opelem;
            src.AstNode body;
            int condseek = 0;
            int opseek = 0;
            int bodyseek = 0;
            initelem = innode.children[2];
            if (initelem.text == ";")
            {
                initelem = null;
                condseek = 3;
            }
            else
            {
                condseek = 4;
            }
            condelem = innode.children[condseek];
            if (condelem.text == ";")
            {
                condelem = null;
                opseek = condseek + 1;
            }
            else
            {
                opseek = condseek + 2;
            }
            opelem = innode.children[opseek];
            if (opelem.text == ")")
            {
                opelem = null;
                bodyseek = opseek + 1;
            }
            else
            {
                bodyseek = opseek + 2;
            }
            body = innode.children[bodyseek];
            if (body.text == ";")
            {
                body = null;
            }


            if (initelem != null)
                ParseNode_CodeAtom(context, initelem, out ffor.elem_init);

            if (condelem != null)
                compiler_expr.ParseNode_Expr(context, condelem, out ffor.elem_cond);

            if (opelem != null)
                ParseNode_CodeAtom(context, opelem, out ffor.elem_step);

            if (body != null)
            {
                ParseNode_CodeAtom(context, body, out var _body);
                if (_body == null)
                    throw new Exception("should not null.");
                ffor.body = _body;
            }

            outnode = ffor;

            context.PopBlock();
        }
        public static void ParseNode_CodeElem_Expr(CompilerContext context, src.AstNode innode, out node.Node_CodeAtom outnode)
        {
            var expr = innode.children[0];//
            //expr 的可能性很多
            compiler_expr.ParseNode_Expr(context, expr, out ttlang.node.Node_Expr _outnode);

            if (_outnode is node.Node_Expr_Call a)
            {
                var outnode_call = new node.Node_Code_Call();
                outnode_call.call_expr = a;
                outnode = outnode_call;
                return;
            }
            else
            {
                //var outnode_call = new node.nod_code();
                //compiler_expr.ParseNode_Expr_Member(context, innode, out outnode);//函数用0表达式解析
                //return;
            }

            //Code_call节点就只是简单的包裹了一个Expr_call
            //因为 code 和 expr 是两条继承链，为了从逻辑上解释清楚，做了一层包裹


            throw new Exception("not support expr type." + innode.text);

        }

    }
}
